/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.shoplink.launcher;

import android.content.Context;
import android.net.NetworkInfo.DetailedState;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.util.Log;

public class AccessPoint {
	static final String TAG = "Settings.AccessPoint";

	private static final String KEY_DETAILEDSTATE = "key_detailedstate";
	private static final String KEY_WIFIINFO = "key_wifiinfo";
	private static final String KEY_SCANRESULT = "key_scanresult";
	private static final String KEY_CONFIG = "key_config";

	private static final int[] STATE_SECURED = { R.attr.state_encrypted };
	private static final int[] STATE_NONE = {};

	/**
	 * These values are matched in string arrays -- changes must be kept in sync
	 */
	public static final int SECURITY_NONE = 0;
	public static final int SECURITY_WEP = 1;
	public static final int SECURITY_PSK = 2;
	public static final int SECURITY_EAP = 3;

	public enum PskType {
		UNKNOWN, WPA, WPA2, WPA_WPA2
	}

	public String ssid;
	public String bssid;
	public int security;
	public int networkId;
	public boolean wpsAvailable = false;

	public PskType pskType = PskType.UNKNOWN;

	private WifiConfiguration mConfig;
	/* package */ScanResult mScanResult;

	private int mRssi;
	private WifiInfo mInfo;
	private DetailedState mState;

	public static int getSecurity(WifiConfiguration config) {
		if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
			return SECURITY_PSK;
		}
		if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP)
				|| config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
			return SECURITY_EAP;
		}
		return (config.wepKeys[0] != null) ? SECURITY_WEP : SECURITY_NONE;
	}

	private static int getSecurity(ScanResult result) {
		if (result.capabilities.contains("WEP")) {
			return SECURITY_WEP;
		} else if (result.capabilities.contains("PSK")) {
			return SECURITY_PSK;
		} else if (result.capabilities.contains("EAP")) {
			return SECURITY_EAP;
		}
		return SECURITY_NONE;
	}

	public boolean update(ScanResult result) {
		if (ssid.equals(result.SSID) && security == getSecurity(result)) {
			if (WifiManager.compareSignalLevel(result.level, mRssi) > 0) {
				int oldLevel = getLevel();
				mRssi = result.level;
			}
			// This flag only comes from scans, is not easily saved in config
			if (security == SECURITY_PSK) {
				pskType = getPskType(result);
			}
			mScanResult = result;
			return true;
		}
		return false;
	}

	public void update(WifiInfo info, DetailedState state) {
		boolean reorder = false;
		if (info != null /* && networkId != WifiConfiguration.INVALID_NETWORK_ID */
				&& networkId == info.getNetworkId()) {
			reorder = (mInfo == null);
			mRssi = info.getRssi();
			mInfo = info;
			mState = state;

		} else if (mInfo != null) {
			reorder = true;
			mInfo = null;
			mState = null;

		}

	}

	public boolean getConnected() {
		return isConnecting;
	}

	private static PskType getPskType(ScanResult result) {
		boolean wpa = result.capabilities.contains("WPA-PSK");
		boolean wpa2 = result.capabilities.contains("WPA2-PSK");
		if (wpa2 && wpa) {
			return PskType.WPA_WPA2;
		} else if (wpa2) {
			return PskType.WPA2;
		} else if (wpa) {
			return PskType.WPA;
		} else {
			Log.w(TAG, "Received abnormal flag string: " + result.capabilities);
			return PskType.UNKNOWN;
		}
	}

	public AccessPoint(Context context, WifiConfiguration config) {
		// setWidgetLayoutResource(R.layout.preference_widget_wifi_signal);
		loadConfig(config);
	}

	public AccessPoint(Context context, ScanResult result) {
		// setWidgetLayoutResource(R.layout.preference_widget_wifi_signal);
		loadResult(result);
	}

	public AccessPoint() {
		// TODO Auto-generated constructor stub
	}

	public AccessPoint(Context context, Bundle savedState) {
		// setWidgetLayoutResource(R.layout.preference_widget_wifi_signal);
		mConfig = savedState.getParcelable(KEY_CONFIG);
		if (mConfig != null) {
			loadConfig(mConfig);
		}
		mScanResult = (ScanResult) savedState.getParcelable(KEY_SCANRESULT);
		if (mScanResult != null) {
			loadResult(mScanResult);
		}
		mInfo = (WifiInfo) savedState.getParcelable(KEY_WIFIINFO);
		if (savedState.containsKey(KEY_DETAILEDSTATE)) {
			mState = DetailedState.valueOf(savedState
					.getString(KEY_DETAILEDSTATE));
		}
	}

	public void saveWifiState(Bundle savedState) {
		savedState.putParcelable(KEY_CONFIG, mConfig);
		savedState.putParcelable(KEY_SCANRESULT, mScanResult);
		savedState.putParcelable(KEY_WIFIINFO, mInfo);
		if (mState != null) {
			savedState.putString(KEY_DETAILEDSTATE, mState.toString());
		}
	}

	public void setWifiState(DetailedState state) {
		this.mState = state;
	}

	public void loadConfig(WifiConfiguration config) {
		ssid = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID));
		bssid = config.BSSID;
		security = getSecurity(config);
		networkId = config.networkId;
		mRssi = Integer.MAX_VALUE;
		mConfig = config;
	}

	public void setWifiConfig(WifiConfiguration config) {
		mConfig = config;
	}
	public void setWifiNetId(int id) {
		networkId = id;
	}

	public void loadResult(ScanResult result) {
		ssid = result.SSID;
		bssid = result.BSSID;
		security = getSecurity(result);
		wpsAvailable = security != SECURITY_EAP
				&& result.capabilities.contains("WPS");
		if (security == SECURITY_PSK)
			pskType = getPskType(result);
		networkId = -1;
		mRssi = result.level;
		mScanResult = result;
	}

	public void setAllResult(ScanResult result, WifiConfiguration config,
			int networkId) {
		if (result != null) {
			bssid = result.BSSID;
			mScanResult = result;
			if (getLevel() < 0) {
				mRssi = result.level;
			}

		}

		if (config != null) {
			bssid = config.BSSID;
			mConfig = config;
		}
		this.networkId = networkId;
	}

	public int getmRssi() {
		return mRssi;
	}

	public int getLevel() {
		if (mRssi == Integer.MAX_VALUE) {
			return -1;
		}
		return WifiManager.calculateSignalLevel(mRssi, 5);
	}

	public WifiConfiguration getConfig() {
		return mConfig;
	}

	public WifiInfo getInfo() {
		return mInfo;
	}

	public DetailedState getState() {
		return mState;
	}

	public static String removeDoubleQuotes(String string) {
		int length = string.length();
		if ((length > 1) && (string.charAt(0) == '"')
				&& (string.charAt(length - 1) == '"')) {
			return string.substring(1, length - 1);
		}
		return string;
	}

	public static String convertToQuotedString(String string) {
		return "\"" + string + "\"";
	}

	/**
	 * Generate and save a default wifiConfiguration with common values. Can
	 * only be called for unsecured networks.
	 * 
	 * @hide
	 */
	public void generateOpenNetworkConfig() {
		if (security != SECURITY_NONE)
			throw new IllegalStateException();
		if (mConfig != null)
			return;
		mConfig = new WifiConfiguration();
		mConfig.SSID = AccessPoint.convertToQuotedString(ssid);
		mConfig.allowedKeyManagement.set(KeyMgmt.NONE);
	}

	public void setConnecting(boolean state) {
		isConnecting = state;
	}

	public String getTitle() {
		return ssid;
	}

	private boolean isConnecting = false;
}
